package com.zyk.scaffold.oauth.controller.token;

import com.alibaba.fastjson.JSON;
import com.idsmanager.dingdang.jwt.DingdangUserRetriever;
import com.zyk.scaffold.common.basic.BasicConstants;
import com.zyk.scaffold.core.domain.Result;
import com.zyk.scaffold.common.exception.BusinessException;
import com.zyk.scaffold.common.utils.AssertUtil;
import com.zyk.scaffold.common.utils.BeanCopyUtil;
import com.zyk.scaffold.common.utils.BusinessUtil;
import com.zyk.scaffold.common.utils.MapUtils;
import com.zyk.scaffold.oauth.config.properties.LocalOauthServerProperties;
import com.zyk.scaffold.oauth.enums.OauthErrorCodeEnum;
import com.zyk.scaffold.oauth.framework.OauthManager;
import com.zyk.scaffold.oauth.framework.domain.AccessToken;
import com.zyk.scaffold.oauth.framework.domain.OauthRequest;
import com.zyk.scaffold.oauth.framework.exception.OauthException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Api(tags = "token管理")
@RestController
@RequestMapping("/token")
public class TokenController {
    @Autowired
    private OauthManager oauthManager;
    @Autowired
    private LocalOauthServerProperties oauthServerProperties;

//    @ApiOperation(value = "登录")
//    @PostMapping("/login")
//    public Result<TokenLoginResponse> login(@RequestBody TokenLoginRequest request) {
//        AccessToken accessToken;
//        try{
//            OauthRequest oauthRequest = BeanCopyUtil.copyProperties(request, OauthRequest::new);
//            accessToken = oauthManager.login(oauthRequest);
//        }catch (OauthException ex){
//            throw new BusinessException(OauthErrorCodeEnum.AUTHENTICATION_ERROR, ex);
//        }
//        TokenLoginResponse tokenLoginResponse = buildTokenLoginResponse(accessToken);
//        return Result.ok(tokenLoginResponse);
//    }

    @ApiOperation(value = "登录-密码登录")
    @PostMapping("/loginPassword")
    public Result<TokenLoginResponse> loginPassword(@RequestBody @Validated TokenLoginPasswordRequest request) {
        AccessToken accessToken;
        try {
            OauthRequest oauthRequest = BeanCopyUtil.copyProperties(request.toTokenLoginRequest(), OauthRequest::new);
            accessToken = oauthManager.login(oauthRequest);
        } catch (OauthException ex) {
            throw new BusinessException(OauthErrorCodeEnum.AUTHENTICATION_ERROR, ex);
        }
        TokenLoginResponse tokenLoginResponse = buildTokenLoginResponse(accessToken);
        return Result.ok(tokenLoginResponse);
    }

    @ApiOperation(value = "登录-刷新登录")
    @PostMapping("/loginRefresh")
    public Result<TokenLoginResponse> loginRefresh(@RequestBody @Validated TokenLoginRefreshRequest request) {
        AccessToken accessToken;
        try {
            OauthRequest oauthRequest = BeanCopyUtil.copyProperties(request.toTokenLoginRequest(), OauthRequest::new);
            accessToken = oauthManager.login(oauthRequest);
        } catch (OauthException ex) {
            throw new BusinessException(OauthErrorCodeEnum.AUTHENTICATION_ERROR, ex);
        }
        TokenLoginResponse tokenLoginResponse = buildTokenLoginResponse(accessToken);
        return Result.ok(tokenLoginResponse);
    }

    @ApiOperation(value = "登录-租户切换")
    @PostMapping("/loginTenantRefresh")
    public Result<TokenLoginResponse> loginTenant(@RequestBody @Validated TokenLoginTenantRequest request) {
        AccessToken accessToken;
        try {
            OauthRequest oauthRequest = BeanCopyUtil.copyProperties(request.toTokenLoginRequest(), OauthRequest::new);
            accessToken = oauthManager.login(oauthRequest);
        } catch (OauthException ex) {
            throw new BusinessException(OauthErrorCodeEnum.AUTHENTICATION_ERROR, ex);
        }
        TokenLoginResponse tokenLoginResponse = buildTokenLoginResponse(accessToken);
        return Result.ok(tokenLoginResponse);
    }

    @ApiOperation(value = "登录-SSO登录")
    @GetMapping("/loginSso")
    public void loginSso(@Valid @NotEmpty @RequestParam("id_token") String ssoToken, HttpServletResponse response) {
        // 失败回调地址
        String url = oauthServerProperties.getSsoJwtAddress();
        OauthRequest oauthRequest = null;
        try {
            String publicKey = oauthServerProperties.getSsoJwtPublicKey();
            DingdangUserRetriever userRetriever = new DingdangUserRetriever(ssoToken, publicKey);
            DingdangUserRetriever.User ssoUser = userRetriever.retrieve();
            if (ssoUser == null) {
                log.error("ssoToken to user infos is null");
            } else {
                oauthRequest = new OauthRequest();
                oauthRequest.setClientId(oauthServerProperties.getScaffoldWebClientId());
                oauthRequest.setGrantType("sso_jwt");
                log.info("sso to user infos:{}", JSON.toJSONString(ssoUser));
                oauthRequest.setAccount(ssoUser.getEmail());
                HashMap<String, String> requestParam = oauthRequest.getRequestParam();
                requestParam.put("name", ssoUser.getName());
                requestParam.put("mobile", ssoUser.getMobile());
                requestParam.put("phoneNumber", ssoUser.getPhoneNumber());
                requestParam.put("email", ssoUser.getEmail());
            }
        } catch (Exception ex) {
            oauthRequest = null;
            log.error("sso parse error,id_token:" + ssoToken, ex);
        }

        if (oauthRequest != null) {
            AccessToken accessToken = oauthManager.login(oauthRequest);
            // 成功回调地址
            url = oauthServerProperties.getSsoSuccessAddress() + "?access_token=" + accessToken.getAccessToken();
        }
        response.setStatus(HttpServletResponse.SC_FOUND);
        response.setHeader(HttpHeaders.LOCATION, url);
    }


    @ApiOperation(value = "用户信息")
    @PostMapping("/info")
    public Result<TokenLoginResponse> info(@RequestHeader(BasicConstants.ACCESS_TOKEN_NAME) String token) {
        AccessToken accessToken;
        try {
            accessToken = oauthManager.getAccessToken(token);
        } catch (OauthException ex) {
            throw new BusinessException(OauthErrorCodeEnum.AUTHENTICATION_ERROR, ex);
        }
        TokenLoginResponse tokenLoginResponse = buildTokenLoginResponse(accessToken);
        return Result.ok(tokenLoginResponse);
    }

    @ApiOperation(value = "登出")
    @PostMapping("logout")
    public Result<Boolean> logout(@RequestHeader(BasicConstants.ACCESS_TOKEN_NAME) String accessToken) {
        AssertUtil.isTrue(oauthManager.logout(accessToken), "登出失败");
        return Result.ok(Boolean.TRUE);
    }

    private TokenLoginResponse buildTokenLoginResponse(AccessToken accessToken) {
        TokenLoginResponse tokenLoginResponse = new TokenLoginResponse();
        tokenLoginResponse.setAccessToken(accessToken.getAccessToken());
        tokenLoginResponse.setExpiresTime(accessToken.getExpiresTime());
        tokenLoginResponse.setExpiresTimeStamp(accessToken.getExpiresTimeStamp());
        tokenLoginResponse.setRefreshToken(accessToken.getRefreshToken());
        tokenLoginResponse.setRefreshExpiresTime(accessToken.getRefreshExpiresTime());
        tokenLoginResponse.setAccount(accessToken.getAccount());
        tokenLoginResponse.setClientId(accessToken.getClientId());
        tokenLoginResponse.setScopes(accessToken.getScopes());
        Map<String, Object> data = accessToken.getData();
        if (CollectionUtils.isEmpty(data)) {
            return tokenLoginResponse;
        }
        tokenLoginResponse.setUserId(MapUtils.getLong(data, "userId"));
        tokenLoginResponse.setNickName(MapUtils.getString(data, "nickName"));
        tokenLoginResponse.setPhone(BusinessUtil.mobileDesensitization(MapUtils.getString(data, "phone")));
        tokenLoginResponse.setEmail(MapUtils.getString(data, "email"));
        tokenLoginResponse.setCreateTime(MapUtils.getString(data, "createTime"));
        tokenLoginResponse.setTenantCode(MapUtils.getString(data, "tenantCode"));
        tokenLoginResponse.setTenantName(MapUtils.getString(data, "tenantName"));
        tokenLoginResponse.setRoles(MapUtils.getArrayList(data, "authorities", String.class));
        List<Map<String, Object>> menuFunctions = (List<Map<String, Object>>) MapUtils.getObject(data, "menuFunctions");
        if (menuFunctions == null) {
            menuFunctions = Collections.emptyList();
        }
        tokenLoginResponse.setMenuFunctions(menuFunctions);
        return tokenLoginResponse;
    }
}
